package beans;

import org.apache.commons.beanutils.BeanUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.beans.*;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

/**
 * {@link java.beans.BeanInfo}
 *
 * @author ChangLiang
 * @date 2020/7/22
 */
public class BeanInfoTest {

    public Map<String,String> map;

    @BeforeEach
    public void setUp() {
        map = new HashMap<>(2);
        // mutable map
        map.put("name", "changliang");
        map.put("age", "32");
    }

    /**
     * not use <code>new Person</code>, but use <code>java reflection</code>
     * and set property value: name=changliang & age=32
     * @throws IntrospectionException
     */
    @Test
    @DisplayName("test Introspector api")
    public void test() throws ClassNotFoundException, IllegalAccessException, InstantiationException, IntrospectionException {

        ///  we must try to avoid this initialization technique as it creates an anonymous extra class at every usage,
        //   and also holds hidden references to the enclosing object and might cause memory leak issues
/*        Map<String, String> map = new HashMap<String, String>(2) {
            {
                put("name", "changliang");
                put("age", "32");
            }
        };*/

        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass("beans.Person");
        Object person = aClass.newInstance();

        // stop Class
        // emit: java.beans.PropertyDescriptor
        // [name=class; propertyType=class java.lang.Class;
        // readMethod=public final native java.lang.Class java.lang.Object.getClass()]
        BeanInfo beanInfo = Introspector.getBeanInfo(Person.class, Object.class);

        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();

        Stream.of(propertyDescriptors).forEach(System.out::println);

        Stream.of(propertyDescriptors).forEach(propertyDescriptor -> {

            map.entrySet().forEach(entry->{
                if (entry.getKey().equals(propertyDescriptor.getName())) {
                    try {
                        if (propertyDescriptor.getPropertyType().equals(Integer.class)) {
                            PropertyEditor editor = new StringToIntegerPropertyEditor();
                            editor.setAsText(entry.getValue());
                            propertyDescriptor.getWriteMethod().invoke(person, editor.getValue());
                        } else if (propertyDescriptor.getPropertyType().equals(String.class)) {
                            propertyDescriptor.getWriteMethod().invoke(person, entry.getValue());
                        } else {
                            throw new RuntimeException("propertyDescriptor type: " + propertyDescriptor.getPropertyType() + " converter not implement yet");
                        }
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            });
        });

        System.out.println(person);
    }

    @Test
    @DisplayName("test BeanUtils encapsulation Introspector&Reflection API")
    public void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Class<?> aClass = Thread.currentThread().getContextClassLoader().loadClass("beans.Person");
        Object person = aClass.newInstance();
        map.entrySet().forEach(entry-> {
            try {
                BeanUtils.setProperty(person,entry.getKey(),entry.getValue());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        });
        System.out.println(person);
    }

    static class StringToIntegerPropertyEditor extends PropertyEditorSupport{
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            Integer value = Integer.valueOf(text);
            setValue(value);
        }
    }
}
